home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / ScrollPane.java < prev    next >
Text File  |  1998-09-22  |  21KB  |  655 lines

  1. /*
  2.  * @(#)ScrollPane.java    1.59 98/07/14
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  * 
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package java.awt;
  15.  
  16. import java.awt.peer.ScrollPanePeer;
  17. import java.awt.event.*;
  18. import java.io.Serializable;
  19.  
  20.  
  21. /**
  22.  * A container class which implements automatic horizontal and/or
  23.  * vertical scrolling for a single child component.  The display
  24.  * policy for the scrollbars can be set to:
  25.  * <OL>
  26.  * <LI>as needed: scrollbars created and shown only when needed by scrollpane
  27.  * <LI>always: scrollbars created and always shown by the scrollpane
  28.  * <LI>never: scrollbars never created or shown by the scrollpane
  29.  * </OL>
  30.  * <P>
  31.  * The state of the horizontal and vertical scrollbars is represented
  32.  * by two objects (one for each dimension) which implement the
  33.  * Adjustable interface.  The API provides methods to access those
  34.  * objects such that the attributes on the Adjustable object (such as unitIncrement,
  35.  * value, etc.) can be manipulated.
  36.  * <P>
  37.  * Certain adjustable properties (minimum, maximum, blockIncrement,
  38.  * and visibleAmount) are set internally by the scrollpane in accordance
  39.  * with the geometry of the scrollpane and its child and these should
  40.  * not be set by programs using the scrollpane.
  41.  * <P>
  42.  * If the scrollbar display policy is defined as "never", then the
  43.  * scrollpane can still be programmatically scrolled using the 
  44.  * setScrollPosition() method and the scrollpane will move and clip
  45.  * the child's contents appropriately.  This policy is useful if the
  46.  * program needs to create and manage its own adjustable controls.
  47.  * <P>
  48.  * The placement of the scrollbars is controlled by platform-specific
  49.  * properties set by the user outside of the program.
  50.  * <P>
  51.  * The initial size of this container is set to 100x100, but can
  52.  * be reset using setSize(). 
  53.  * <P>
  54.  * Insets are used to define any space used by scrollbars and any
  55.  * borders created by the scroll pane. getInsets() can be used
  56.  * to get the current value for the insets.  If the value of 
  57.  * scrollbarsAlwaysVisible is false, then the value of the insets
  58.  * will change dynamically depending on whether the scrollbars are
  59.  * currently visible or not.    
  60.  *
  61.  * @version     1.59 07/14/98
  62.  * @author      Tom Ball
  63.  * @author      Amy Fowler
  64.  * @author      Tim Prinzing
  65.  */
  66. public class ScrollPane extends Container {
  67.  
  68.     /**
  69.      * Specifies that horizontal/vertical scrollbar should be shown 
  70.      * only when the size of the child exceeds the size of the scrollpane
  71.      * in the horizontal/vertical dimension.
  72.      */
  73.     public static final int SCROLLBARS_AS_NEEDED = 0;
  74.  
  75.     /**
  76.      * Specifies that horizontal/vertical scrollbars should always be
  77.      * shown regardless of the respective sizes of the scrollpane and child.
  78.      */
  79.     public static final int SCROLLBARS_ALWAYS = 1;
  80.  
  81.     /**
  82.      * Specifies that horizontal/vertical scrollbars should never be shown
  83.      * regardless of the respective sizes of the scrollpane and child.
  84.      */
  85.     public static final int SCROLLBARS_NEVER = 2;
  86.  
  87.     private int scrollbarDisplayPolicy;
  88.     private ScrollPaneAdjustable vAdjustable;
  89.     private ScrollPaneAdjustable hAdjustable;
  90.  
  91.     private static final String base = "scrollpane";
  92.     private static int nameCounter = 0;
  93.  
  94.     /*
  95.      * JDK 1.1 serialVersionUID 
  96.      */
  97.      private static final long serialVersionUID = 7956609840827222915L;
  98.  
  99.     /**
  100.      * Create a new scrollpane container with a scrollbar display policy of
  101.      * "as needed".
  102.      */
  103.     public ScrollPane() {
  104.     this(SCROLLBARS_AS_NEEDED);
  105.     }
  106.  
  107.     /**
  108.      * Create a new scrollpane container.
  109.      * @param scrollbarDisplayPolicy policy for when scrollbars should be shown
  110.      */
  111.     public ScrollPane(int scrollbarDisplayPolicy) {
  112.     this.layoutMgr = null;
  113.     this.width = 100;
  114.     this.height = 100;
  115.     switch (scrollbarDisplayPolicy) {
  116.       case SCROLLBARS_NEVER:
  117.       case SCROLLBARS_AS_NEEDED:
  118.       case SCROLLBARS_ALWAYS:
  119.         this.scrollbarDisplayPolicy = scrollbarDisplayPolicy;
  120.         break;
  121.       default:
  122.         throw new IllegalArgumentException("illegal scrollbar display policy");
  123.     }
  124.  
  125.     vAdjustable = new ScrollPaneAdjustable(this, new PeerFixer(this), 
  126.                            Adjustable.VERTICAL);
  127.     hAdjustable = new ScrollPaneAdjustable(this, new PeerFixer(this), 
  128.                            Adjustable.HORIZONTAL);
  129.     }
  130.  
  131.     /**
  132.      * Construct a name for this component.  Called by getName() when the
  133.      * name is null.
  134.      */
  135.     String constructComponentName() {
  136.         return base + nameCounter++;
  137.     }
  138.  
  139.     /** 
  140.      * Adds the specified component to this scroll pane container.
  141.      * If the scroll pane has an existing child component, that
  142.      * component is removed and the new one is added.  
  143.      * @param comp the component to be added 
  144.      * @param constraints  not applicable
  145.      * @param index position of child component (must be <= 0) 
  146.      */
  147.     protected final void addImpl(Component comp, Object constraints, int index) {
  148.         synchronized (getTreeLock()) {
  149.         if (getComponentCount() > 0) {
  150.         remove(0);
  151.         }
  152.         if (index > 0) {
  153.         throw new IllegalArgumentException("position greater than 0");
  154.         }
  155.         
  156.         super.addImpl(comp, constraints, index);
  157.     }
  158.     }
  159.  
  160.     /**
  161.      * Returns the display policy for the scrollbars.
  162.      * @return the display policy for the scrollbars
  163.      */
  164.     public int getScrollbarDisplayPolicy() {
  165.         return scrollbarDisplayPolicy;
  166.     }
  167.  
  168.     /**
  169.      * Returns the current size of the scroll pane's view port.
  170.      * @return the size of the view port in pixels
  171.      */
  172.     public Dimension getViewportSize() {
  173.     Insets i = getInsets();
  174.     return new Dimension(width - i.right - i.left,
  175.                  height - i.top - i.bottom);
  176.     }
  177.  
  178.     /**
  179.      * Returns the height that would be occupied by a horizontal
  180.      * scrollbar, which is independent of whether it is currently
  181.      * displayed by the scroll pane or not.
  182.      * @return the height of a horizontal scrollbar in pixels
  183.      */
  184.     public int getHScrollbarHeight() {
  185.     int h = 0;
  186.     if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) {
  187.         ScrollPanePeer peer = (ScrollPanePeer)this.peer;
  188.         if (peer != null) {
  189.         h = peer.getHScrollbarHeight();
  190.         }
  191.     }
  192.     return h;
  193.     }
  194.  
  195.     /**
  196.      * Returns the width that would be occupied by a vertical
  197.      * scrollbar, which is independent of whether it is currently
  198.      * displayed by the scroll pane or not.
  199.      * @return the width of a vertical scrollbar in pixels
  200.      */
  201.     public int getVScrollbarWidth() {
  202.     int w = 0;
  203.     if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) {
  204.         ScrollPanePeer peer = (ScrollPanePeer)this.peer;
  205.         if (peer != null) {
  206.         w = peer.getVScrollbarWidth();
  207.         }
  208.     }
  209.     return w;
  210.     }
  211.  
  212.     /**
  213.      * Returns the Adjustable object which represents the state of
  214.      * the vertical scrollbar. If the scrollbar display policy is "never",
  215.      * this method returns null.
  216.      */
  217.     public Adjustable getVAdjustable() {
  218.         return vAdjustable;
  219.     }
  220.  
  221.     /**
  222.      * Returns the Adjustable object which represents the state of
  223.      * the horizontal scrollbar.  If the scrollbar display policy is "never",
  224.      * this method returns null.
  225.      */
  226.     public Adjustable getHAdjustable() {
  227.         return hAdjustable;
  228.     }
  229.  
  230.     /**
  231.      * Scrolls to the specified position within the child component.
  232.      * A call to this method is only valid if the scroll pane contains
  233.      * a child.  Specifying a position outside of the legal scrolling bounds
  234.      * of the child will scroll to the closest legal position.  
  235.      * Legal bounds are defined to be the rectangle: 
  236.      * x = 0, y = 0, width = (child width - view port width),
  237.      * height = (child height - view port height).
  238.      * This is a convenience method which interfaces with the Adjustable
  239.      * objects which represent the state of the scrollbars.
  240.      * @param x the x position to scroll to
  241.      * @param y the y position to scroll to
  242.      * @exception IllegalArgumentException if specified coordinates are
  243.      * not within the legal scrolling bounds of the child component.
  244.      */
  245.     public void setScrollPosition(int x, int y) {
  246.         synchronized (getTreeLock()) {
  247.         if (ncomponents <= 0) {
  248.         throw new NullPointerException("child is null");
  249.         }
  250.         hAdjustable.setValue(x);
  251.         vAdjustable.setValue(y);
  252.     }
  253.     }
  254.  
  255.    /**
  256.      * Scrolls to the specified position within the child component.
  257.      * A call to this method is only valid if the scroll pane contains
  258.      * a child and the specified position is within legal scrolling bounds
  259.      * of the child.  Legal bounds are defined to be the rectangle: 
  260.      * x = 0, y = 0, width = (child width - view port width),
  261.      * height = (child height - view port height).
  262.      * This is a convenience method which interfaces with the Adjustable
  263.      * objects which represent the state of the scrollbars.
  264.      * @param p the Point representing the position to scroll to
  265.      * @exception IllegalArgumentException if specified coordinates are
  266.      * not within the legal scrolling bounds of the child component.
  267.      */
  268.     public void setScrollPosition(Point p) {
  269.         setScrollPosition(p.x, p.y);
  270.     }
  271.  
  272.     /**
  273.      * Returns the current x,y position within the child which is displayed 
  274.      * at the 0,0 location of the scrolled panel's view port.
  275.      * This is a convenience method which interfaces with the adjustable
  276.      * objects which represent the state of the scrollbars.
  277.      * @return the coordinate position for the current scroll position
  278.      */
  279.     public Point getScrollPosition() {
  280.     if (ncomponents <= 0) {
  281.         throw new NullPointerException("child is null");
  282.     }
  283.     return new Point(hAdjustable.getValue(), vAdjustable.getValue()); 
  284.     }
  285.  
  286.     /** 
  287.      * Sets the layout manager for this container.  This method is
  288.      * overridden to prevent the layout mgr from being set.
  289.      * @param mgr the specified layout manager
  290.      */
  291.     public final void setLayout(LayoutManager mgr) {
  292.     throw new AWTError("ScrollPane controls layout");
  293.     }
  294.  
  295.     /**
  296.      * Lays out this container by resizing its child to its preferred size.
  297.      * If the new preferred size of the child causes the current scroll
  298.      * position to be invalid, the scroll position is set to the closest
  299.      * valid position.
  300.      *
  301.      * @see Component#validate
  302.      */
  303.     public void doLayout() {
  304.     layout();
  305.     }
  306.  
  307.     /**
  308.      * Determine the size to allocate the child component.
  309.      * If the viewport area is bigger than the childs 
  310.      * preferred size then the child is allocated enough
  311.      * to fill the viewport, otherwise the child is given
  312.      * it's preferred size.
  313.      */
  314.     Dimension calculateChildSize() {
  315.     //
  316.     // calculate the view size, accounting for border but not scrollbars
  317.     // - don't use right/bottom insets since they vary depending
  318.     //   on whether or not scrollbars were displayed on last resize
  319.     //
  320.     Dimension    size = getSize();
  321.     Insets        insets = getInsets();
  322.     int         viewWidth = size.width - insets.left*2;
  323.     int         viewHeight = size.height - insets.top*2;
  324.  
  325.     //
  326.     // determine whether or not horz or vert scrollbars will be displayed
  327.     //
  328.     boolean vbarOn;
  329.     boolean hbarOn;
  330.     Component child = getComponent(0);
  331.     Dimension childSize = new Dimension(child.getPreferredSize());
  332.  
  333.     if (scrollbarDisplayPolicy == SCROLLBARS_AS_NEEDED) {
  334.         vbarOn = childSize.height > viewHeight;
  335.         hbarOn = childSize.width  > viewWidth;
  336.     } else if (scrollbarDisplayPolicy == SCROLLBARS_ALWAYS) {
  337.         vbarOn = hbarOn = true;
  338.     } else { // SCROLLBARS_NEVER
  339.         vbarOn = hbarOn = false;
  340.     }
  341.     
  342.     //
  343.     // adjust predicted view size to account for scrollbars
  344.     //
  345.     int vbarWidth = getVScrollbarWidth(); 
  346.     int hbarHeight = getHScrollbarHeight();
  347.     if (vbarOn) {
  348.         viewWidth -= vbarWidth;
  349.     }
  350.     if(hbarOn) {
  351.         viewHeight -= hbarHeight;
  352.     }
  353.  
  354.     //
  355.     // if child is smaller than view, size it up
  356.     //
  357.     if (childSize.width < viewWidth) {
  358.         childSize.width = viewWidth;
  359.     }
  360.     if (childSize.height < viewHeight) {
  361.         childSize.height = viewHeight;
  362.     }
  363.  
  364.     return childSize;
  365.     }
  366.  
  367.     /** 
  368.      * @deprecated As of JDK version 1.1,
  369.      * replaced by <code>doLayout()</code>.
  370.      */
  371.     public void layout() {
  372.     if (ncomponents > 0) {
  373.         Component c = getComponent(0);
  374.         Point p = getScrollPosition();
  375.         Dimension cs = calculateChildSize();
  376.         Dimension vs = getViewportSize();    
  377.         Insets i = getInsets();
  378.         
  379.         c.reshape(i.left - p.x, i.top - p.y, cs.width, cs.height);
  380.         ScrollPanePeer peer = (ScrollPanePeer)this.peer;
  381.         if (peer != null) {
  382.             peer.childResized(cs.width, cs.height);
  383.         }
  384.  
  385.         // update adjustables... the viewport size may have changed
  386.         // with the scrollbars coming or going so the viewport size
  387.         // is updated before the adjustables.
  388.         vs = getViewportSize();
  389.         hAdjustable.setSpan(0, cs.width, vs.width);
  390.         vAdjustable.setSpan(0, cs.height, vs.height);
  391.     }
  392.     }
  393.  
  394.     /** 
  395.      * Prints the component in this scroll pane.
  396.      * @param g the specified Graphics window
  397.      * @see Component#print
  398.      * @see Component#printAll
  399.      */
  400.     public void printComponents(Graphics g) {
  401.     if (ncomponents > 0) {
  402.         Component c = component[0];
  403.         Point p = c.getLocation();
  404.         Dimension vs = getViewportSize();
  405.         Insets i = getInsets();
  406.  
  407.         Graphics cg = g.create();
  408.         try {
  409.             cg.clipRect(i.left, i.top, vs.width, vs.height);
  410.             cg.translate(p.x, p.y);
  411.         c.printAll(cg);
  412.         } finally {
  413.         cg.dispose();
  414.         }
  415.     }
  416.     }
  417.  
  418.     /**
  419.      * Creates the scroll pane's peer. 
  420.      */
  421.     public void addNotify() {
  422.         synchronized (getTreeLock()) {
  423.  
  424.             int vAdjustableValue = 0;
  425.             int hAdjustableValue = 0;
  426.  
  427.             // Bug 4124460. Save the current adjustable values,
  428.             // so they can be restored after addnotify. Set the
  429.             // adjustibles to 0, to prevent crashes for possible
  430.             // negative values.
  431.             if (getComponentCount() > 0) {
  432.                 vAdjustableValue = vAdjustable.getValue();
  433.                 hAdjustableValue = hAdjustable.getValue();
  434.                 vAdjustable.setValue(0);
  435.                 hAdjustable.setValue(0);
  436.             }
  437.  
  438.         if (peer == null)
  439.             peer = getToolkit().createScrollPane(this);
  440.  
  441.         super.addNotify();
  442.  
  443.             // Bug 4124460. Restore the adjustable values.
  444.         if (getComponentCount() > 0) {
  445.                 vAdjustable.setValue(vAdjustableValue);
  446.                 hAdjustable.setValue(hAdjustableValue);
  447.             }
  448.  
  449.         if (getComponentCount() > 0) {
  450.         Component comp = getComponent(0);
  451.         if (comp.peer instanceof java.awt.peer.LightweightPeer) {
  452.             // The scrollpane won't work with a windowless child... it assumes
  453.             // it is moving a child window around so the windowless child is
  454.             // wrapped with a window.
  455.             remove(0);
  456.             Panel child = new Panel();
  457.             child.setLayout(new BorderLayout());
  458.             child.add(comp);
  459.             add(child);
  460.         }
  461.         }
  462.         }
  463.     }
  464.  
  465.     public String paramString() {
  466.     String sdpStr;
  467.     switch (scrollbarDisplayPolicy) {
  468.         case SCROLLBARS_AS_NEEDED:
  469.         sdpStr = "as-needed";
  470.         break;
  471.         case SCROLLBARS_ALWAYS:
  472.         sdpStr = "always";
  473.         break;
  474.         case SCROLLBARS_NEVER:
  475.         sdpStr = "never";
  476.         break;
  477.         default:
  478.         sdpStr = "invalid display policy";
  479.     }
  480.     Point p = ncomponents > 0? getScrollPosition() : new Point(0,0);
  481.     Insets i = getInsets();        
  482.     return super.paramString()+",ScrollPosition=("+p.x+","+p.y+")"+
  483.         ",Insets=("+i.top+","+i.left+","+i.bottom+","+i.right+")"+
  484.         ",ScrollbarDisplayPolicy="+sdpStr;
  485.     }
  486.  
  487.     class PeerFixer implements AdjustmentListener, java.io.Serializable {
  488.  
  489.     PeerFixer(ScrollPane scroller) {
  490.         this.scroller = scroller;
  491.     }
  492.  
  493.     /**
  494.      * Invoked when the value of the adjustable has changed.
  495.      */   
  496.         public void adjustmentValueChanged(AdjustmentEvent e) {
  497.         Adjustable adj = e.getAdjustable();
  498.         int value = e.getValue();
  499.         ScrollPanePeer peer = (ScrollPanePeer) scroller.peer;
  500.         if (peer != null) {
  501.         peer.setValue(adj, value);
  502.         }
  503.         
  504.         Component c = scroller.getComponent(0);
  505.         switch(adj.getOrientation()) {
  506.         case Adjustable.VERTICAL:
  507.         c.move(c.getLocation().x, -(value));
  508.         break;
  509.         case Adjustable.HORIZONTAL:
  510.         c.move(-(value), c.getLocation().y);
  511.         break;
  512.         default:
  513.         throw new IllegalArgumentException("Illegal adjustable orientation");
  514.         }
  515.     }
  516.  
  517.         private ScrollPane scroller;
  518.     }
  519. }
  520.  
  521.  
  522. class ScrollPaneAdjustable implements Adjustable, java.io.Serializable {
  523.  
  524.     private ScrollPane sp;
  525.     private int orientation;
  526.     private int minimum;
  527.     private int maximum;
  528.     private int visibleAmount;
  529.     private int unitIncrement = 1;
  530.     private int blockIncrement = 1;
  531.     private int value;
  532.     private AdjustmentListener adjustmentListener;
  533.  
  534.     private static final String SCROLLPANE_ONLY = 
  535.         "Can be set by scrollpane only";
  536.  
  537.     /*
  538.      * JDK 1.1 serialVersionUID 
  539.      */
  540.     private static final long serialVersionUID = -3359745691033257079L;
  541.  
  542.     public ScrollPaneAdjustable(ScrollPane sp, AdjustmentListener l, int orientation) {
  543.         this.sp = sp;
  544.         this.orientation = orientation;
  545.     addAdjustmentListener(l);
  546.     }
  547.  
  548.     /**
  549.      * This is called by the scrollpane itself to update the 
  550.      * min,max,visible values.  The scrollpane is the only one 
  551.      * that should be changing these since it is the source of
  552.      * these values.
  553.      */
  554.     void setSpan(int min, int max, int visible) {
  555.     // adjust the values to be reasonable
  556.     minimum = min;
  557.     maximum = Math.max(max, minimum + 1);
  558.     visibleAmount = Math.min(visible, maximum - minimum);
  559.     visibleAmount = Math.max(visibleAmount, 1);
  560.         blockIncrement = Math.max((int)(visible * .90), 1);
  561.     setValue(value);
  562.     }
  563.  
  564.     public int getOrientation() {
  565.         return orientation;
  566.     }
  567.  
  568.     public void setMinimum(int min) {
  569.     throw new AWTError(SCROLLPANE_ONLY);
  570.     }
  571.  
  572.     public int getMinimum() {
  573.         return 0;
  574.     }
  575.  
  576.     public void setMaximum(int max) {
  577.     throw new AWTError(SCROLLPANE_ONLY);
  578.     }   
  579.  
  580.     public int getMaximum() {
  581.         return maximum;
  582.     }
  583.  
  584.     public synchronized void setUnitIncrement(int u) {
  585.     if (u != unitIncrement) {
  586.         unitIncrement = u;
  587.         if (sp.peer != null) {
  588.         ScrollPanePeer peer = (ScrollPanePeer) sp.peer;
  589.         peer.setUnitIncrement(this, u);
  590.         }
  591.     }
  592.     } 
  593.   
  594.     public int getUnitIncrement() {
  595.         return unitIncrement;
  596.     }
  597.  
  598.     public synchronized void setBlockIncrement(int b) {
  599.         blockIncrement = b;
  600.     }    
  601.  
  602.     public int getBlockIncrement() {
  603.         return blockIncrement;
  604.     }
  605.  
  606.     public void setVisibleAmount(int v) {
  607.     throw new AWTError(SCROLLPANE_ONLY);
  608.     }
  609.  
  610.     public int getVisibleAmount() {
  611.         return visibleAmount;
  612.     }
  613.  
  614.     public void setValue(int v) {
  615.     // bounds check
  616.     v = Math.max(v, minimum);
  617.     v = Math.min(v, maximum - visibleAmount);
  618.  
  619.         if (v != value) {
  620.         value = v;
  621.         // Synchronously notify the listeners so that they are 
  622.         // guaranteed to be up-to-date with the Adjustable before
  623.         // it is mutated again.
  624.         AdjustmentEvent e = 
  625.         new AdjustmentEvent(this, AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
  626.                     AdjustmentEvent.TRACK, value);
  627.         adjustmentListener.adjustmentValueChanged(e);
  628.     }
  629.     }
  630.  
  631.     public int getValue() {
  632.         return value;
  633.     }
  634.  
  635.     public synchronized void addAdjustmentListener(AdjustmentListener l) {
  636.     adjustmentListener = AWTEventMulticaster.add(adjustmentListener, l);
  637.     }
  638.  
  639.     public synchronized void removeAdjustmentListener(AdjustmentListener l){
  640.     adjustmentListener = AWTEventMulticaster.remove(adjustmentListener, l);
  641.     }
  642.  
  643.     public String toString() {
  644.     return getClass().getName() + "[" + paramString() + "]";
  645.     }
  646.  
  647.     public String paramString() {
  648.         return ((orientation==Adjustable.VERTICAL?"vertical,":"horizontal,")+
  649.           "[0.."+maximum+"],"+"val="+value+",vis="+visibleAmount+
  650.                 ",unit="+unitIncrement+",block="+blockIncrement);
  651.     }
  652.  
  653. }
  654.  
  655.